#include "scenebasic.h"

#include <cstdio>
#include <cstdlib>

#include <fstream>
using std::ifstream;
#include <sstream>
using std::ostringstream;

#include "glutils.h"
#include <openctm.h>
#include <glshadermanager.h>
#include <glm/gtx/transform.hpp>

SceneBasic::SceneBasic()
{
}

SceneBasic::~SceneBasic()
{
    glDeleteVertexArrays(1, &vaoHandle);
    glDeleteBuffers(3, vboHandles);
}

bool SceneBasic::loadmesh(const char *filename)
{
    // Open the CTM file:
    CTMcontext ctmContext = ctmNewContext(CTM_IMPORT);
    ctmLoad(ctmContext, filename);

    if (ctmGetError(ctmContext) != CTM_NONE)
        return false;

    CTMuint vertexCount = ctmGetInteger(ctmContext, CTM_VERTEX_COUNT);

    //delete vertex array and buffer
    glDeleteVertexArrays(1, &vaoHandle);
    glDeleteBuffers(3, vboHandles);
    glGenBuffers(3, vboHandles);
    GLuint positionBufferHandle = vboHandles[0];
    GLuint normalBufferHandle = vboHandles[1];
    GLuint indicesBufferHandle = vboHandles[2];

    glGenVertexArrays( 1, &vaoHandle );
    glBindVertexArray(vaoHandle);

        // Create the VBO for positions:
        const CTMfloat* positions = ctmGetFloatArray(ctmContext, CTM_VERTICES);
        if (positions) {
            GLsizeiptr size = vertexCount * sizeof(float) * 3;
            glBindBuffer(GL_ARRAY_BUFFER, positionBufferHandle);
            glBufferData(GL_ARRAY_BUFFER, size, positions, GL_STATIC_DRAW);
            glEnableVertexAttribArray(0);
            glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, 0);
        }

        // Create the VBO for normals:
        const CTMfloat* normals = ctmGetFloatArray(ctmContext, CTM_NORMALS);
        if (normals) {
            GLsizeiptr size = vertexCount * sizeof(float) * 3;
            glBindBuffer(GL_ARRAY_BUFFER, normalBufferHandle);
            glBufferData(GL_ARRAY_BUFFER, size, normals, GL_STATIC_DRAW);
            glEnableVertexAttribArray(1);
            glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, 0);
        }

        // Create the VBO for indices:
        const CTMuint* indices = ctmGetIntegerArray(ctmContext, CTM_INDICES);
        numIndices = 3 * ctmGetInteger(ctmContext, CTM_TRIANGLE_COUNT);
        if (indices) {
            GLsizeiptr size = numIndices * sizeof(CTMuint);
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesBufferHandle);
            glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, indices, GL_STATIC_DRAW);
        }

    glBindVertexArray(0);

    ctmFreeContext(ctmContext);
    return true;
}

void SceneBasic::initScene()
{
    /////////////////// Create the VBO ////////////////////
    float positionData[] = {
        -0.8f, -0.8f, 0.0f,
         0.8f, -0.8f, 0.0f,
         0.0f,  0.8f, 0.0f };
    float colorData[] = {
        1.0f, 0.0f, 0.0f,
        0.0f, 1.0f, 0.0f,
        0.0f, 0.0f, 1.0f };
    unsigned int indiceData[] = {
        0, 1, 2
    };

    // Create and populate the buffer objects
    glGenBuffers(3, vboHandles);
    GLuint positionBufferHandle = vboHandles[0];
    GLuint colorBufferHandle = vboHandles[1];
    GLuint indicesBufferHandle = vboHandles[2];

    glBindBuffer(GL_ARRAY_BUFFER, positionBufferHandle);
    glBufferData(GL_ARRAY_BUFFER, 9 * sizeof(float), positionData, GL_STATIC_DRAW);

    glBindBuffer(GL_ARRAY_BUFFER, colorBufferHandle);
    glBufferData(GL_ARRAY_BUFFER, 9 * sizeof(float), colorData, GL_STATIC_DRAW);

    numIndices = 3;
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesBufferHandle);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, 3 * sizeof(unsigned int), indiceData, GL_STATIC_DRAW);

    // Create and set-up the vertex array object
    glGenVertexArrays( 1, &vaoHandle );
    glBindVertexArray(vaoHandle);
        glEnableVertexAttribArray(0);  // Vertex position
        glEnableVertexAttribArray(1);  // Vertex color

        glBindBuffer(GL_ARRAY_BUFFER, positionBufferHandle);
        glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, 0, (GLubyte *)NULL );

        glBindBuffer(GL_ARRAY_BUFFER, colorBufferHandle);
        glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, 0, (GLubyte *)NULL );

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesBufferHandle);
    glBindVertexArray(0);


    /////////////////setup transformation matrix//////////////////////
    glClearColor(0.f, 0.f, 0.f, 1.f);
    glClearDepth(1.f);
    glEnable(GL_DEPTH_TEST);

//    model = glm::mat4(1.f);
//    model *= glm::rotate(-35.f, glm::vec3(1.f, 0.f, 0.f));
//    model *= glm::rotate(35.f, glm::vec3(0.f, 1.f, 0.f));
//    view = glm::lookAt(glm::vec3(0.f, 0.f, 2.f), glm::vec3(0.f, 0.f, 0.f), glm::vec3(0.f, 1.f, 0.f));
//    mv = view*model;
//    projection = glm::mat4(1.f);
}

void SceneBasic::setMatrices(float *r, float *t)
{
    model = glm::mat4(1.f);
    model *= glm::rotate(r[0], glm::vec3(1.f, 0.f, 0.f));
    model *= glm::rotate(r[1], glm::vec3(0.f, 1.f, 0.f));
    model *= glm::rotate(r[2], glm::vec3(0.f, 0.f, 1.f));

    view = glm::translate(glm::mat4(1.f), glm::vec3(t[0], t[1], t[2]));

    mv = view*model;
}

void SceneBasic::update( float t )
{

}

void SceneBasic::render()
{
    glShader* shader;
    if (!(shader = (*glShaderManager::getInstance())["simple"])) return;

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    shader->begin();
        //set matrices
        shader->setUniform("ModelViewMatrix", mv);
        shader->setUniform("NormalMatrix", glm::mat3(glm::vec3(mv[0]), glm::vec3(mv[1]), glm::vec3(mv[2])));
        shader->setUniform("MVP", projection * mv);
        //set parameters
        shader->setUniform3f("Kd", 0.9f, 0.5f, 0.3f);
        shader->setUniform("Ld", 1.f, 1.f, 1.f);
        shader->setUniform("LightPosition", view * glm::vec4(5.f, 5.f, 2.f, 1.f));

        glBindVertexArray(vaoHandle);
        //glDrawArrays(GL_TRIANGLES, 0, 3 );
        glDrawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_INT, (void*)0);
    shader->end();
}

void SceneBasic::resize(int w, int h)
{
    glViewport(0,0,w,h);
    projection = glm::perspective(70.f, (float)w/h, 0.1f, 1000.f);
}
